package digitRecognitionProblem.findMlpArgs;

import genetic_algorithm.Chromosome;
import genetic_algorithm.Crossover;
import genetic_algorithm.FitnessFunction;
import genetic_algorithm.GeneticAlgorithm;
import genetic_algorithm.Mutation;
import genetic_algorithm.Population;
import genetic_algorithm.Selection;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.List;

import mlp.Mlp;
import mlp.MlpExamples;
import utils.GAretVal;

/**
 * Runs a genetic algorithm which finds optimal number of neurons in the hidden layer 
 * and range of initial weights of a neural network which recognizes the sepcified digit.
 * ATTENTION: before running, the activation function of mlp.Neuron.java must be set to ReLu
 * and mlp.NEGATIVE should be 0
 */
public class MlpArgsMain {

	// indices for main arguments
	private enum ArgNums {
		DIGIT, MAX_GENS, POP_SIZE, ELITE_SIZE, MUT_RATE, LEN_HIDDEN, NUM_EPOCHS
	}
	
	private static int MAX_GENS = 10; // maximal number of generations to run
	private static final int CROSSOVER_MODE = MlpArgsCrossover.SINGLE_POINT; // the mode of DigitRecognitionCrossover;
	private static final int SELECTION_MODE = MlpArgsSelection.ROULETTE; // selection phase mode
	private static final int TOURNAMENT_SIZE = 5; // how many chromosomes to include in tournament (if such mode is chosen for selection)
	private static int POP_SIZE = 25; // number of chromosomes in the population
	private static final double CROSSOVER_RATE = 0.9; // chance to perform crossover
	private static final float LEARNING_RATE = 0.01f; // learning rate for back propagation
	private static final float MOMENTUM = 0.2f; // momentum weight for back propagation
	private static double MUTATION_RATE = 0.25; // chance to perform mutation
	private static int NUM_EPOCHS = 15; // number of iterations to run back-propagation after creating a new chromosome
	private static final int NUM_TRIES = 1; // how many times should try a chromosome while calculating its fitness
	public static final String TRAIN_FILE = "train_reduce.csv"; // path to train file	
	private static final int TRAIN_AMOUNT = 300; // how many train examples to use
	private static final String TEST_FILE = "validate1_reduce.csv"; //"test_reduce.txt"; // path to test file
	private static boolean REMOVE_PARENTS = false; // if chromosomes can be selected only once
	private static int ELITE_SIZE = 2; // size of elite chromosomes
	public static int DIGIT = 9; // digit to learn
	public static int LEN_HIDDEN_BITS = 8; // number of bits encoding how many neurons are in hidden layer
	public static float THRESHOLD = 0.9f; // threshold for a network's output
		
	public static void main(String[] args) {
	
		// parse arguments
		DIGIT = Integer.parseInt(args[ArgNums.DIGIT.ordinal()]);
		MAX_GENS = Integer.parseInt(args[ArgNums.MAX_GENS.ordinal()]); 
		POP_SIZE = Integer.parseInt(args[ArgNums.POP_SIZE.ordinal()]);
		ELITE_SIZE = Integer.parseInt(args[ArgNums.ELITE_SIZE.ordinal()]);
		MUTATION_RATE = Double.parseDouble(args[ArgNums.MUT_RATE.ordinal()]);
		LEN_HIDDEN_BITS = Integer.parseInt(args[ArgNums.LEN_HIDDEN.ordinal()]);
		NUM_EPOCHS = Integer.parseInt(args[ArgNums.NUM_EPOCHS.ordinal()]);

		// redirect output to file
		try {
			File file = new File("Details_rec_" + DIGIT + ".txt");
			FileOutputStream fos;
			fos = new FileOutputStream(file);
			PrintStream ps = new PrintStream(fos);
			System.setOut(ps);
			System.setErr(ps);
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
				
		// set global parameters for neural network
		Mlp.LEARNING_RATE = LEARNING_RATE;
		Mlp.MOMENTUM = MOMENTUM;
		Mlp.NUMBER_EXAMPLE = TRAIN_AMOUNT;
		
		// parse test examples
		MlpExamples test = new MlpExamples(TEST_FILE, DIGIT);
		
		System.out.println("MlpArgsMain.main(): train and test examples are loaded");
		
		// allocate objects to implement each phase of the genetic algorithm
		FitnessFunction fitnessFunction = new MlpArgsFitness(TRAIN_AMOUNT, test, NUM_EPOCHS, NUM_TRIES);		
		Selection selector = new MlpArgsSelection(REMOVE_PARENTS, SELECTION_MODE, TOURNAMENT_SIZE);
		Crossover crossover = new MlpArgsCrossover(CROSSOVER_MODE);
		Mutation mutation = new MlpArgsMutation();
		
		System.out.println("MlpArgsMain.main(): initializing population");
		
		// create initial population
		List<Chromosome> chromList = new LinkedList<Chromosome>();
		for (int i = 0; i < POP_SIZE; ++i) {
			chromList.add(new MlpArgsChromosome());
		}
		Population population = new Population(chromList, fitnessFunction);
		
		System.out.println("MlpArgsMain.main(): population is set");
		
		// initialize the genetic algorithm
		GeneticAlgorithm GA = new GeneticAlgorithm(fitnessFunction, crossover,
				selector, mutation, CROSSOVER_RATE, MUTATION_RATE);
		
		// find optimal solution
		GAretVal result = GA.execute(population, MAX_GENS, true, ELITE_SIZE);
		
		// print best chromosome's details
		System.out.println("result chromosome: " + result.getOptSolution());
		
		// generate csv for execution data
		result.createsCsvFiles("Weight_range_Hidden_layer_digit_" + DIGIT);
	}

}
